
#include "tf_list.h"
#include "tf_log.h"
#include "tf_mem.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#if defined( __cplusplus)
extern "C"{
#endif

/**
 *  Allocate a array_list
 *  parameters: len=>length
 *  return: NULL if failed, else a valid ptr
 */
array_list* alloc_array_list( size_t len){
    ENTER_FUNC;
    array_list* lst=( array_list*) tf_malloc( sizeof( array_list));
    lst->entries=NULL;
    lst->length=0;

    if( lst!=NULL){
        lst->length=len;
        if( len>0){
            lst->entries=( list_entry*) tf_malloc( sizeof(list_entry)* (lst->length));
            if( lst->entries!=NULL){ 
                memset( lst->entries, sizeof(list_entry) * (lst->length), 0);

            }else{
                ERR_LOG("mem alloc failed");
            }
        }else{
            lst->entries=NULL;
            lst->length=0;
        }
    }else{
        ERR_LOG("mem alloc failed");
    }

    LEAVE_FUNC;
    return lst;
}

/**
 * Free a array_list, the array_list structure will also be freed
 * parameter: lst=> list to be freed
 * return: 0=>OK, !0=>Error
 */
int free_array_list( array_list* lst){
    ENTER_FUNC;
    if( lst==NULL){
        ERR_LOG( "Cannot free NULL array_list ptr");
        LEAVE_FUNC;
        return -1;
    }

    int i=0;
    if( lst->entries!=NULL){
        for( i=0; i<lst->length; ++i){
            if( lst->entries[i].data!=NULL){
                tf_free( lst->entries[i].data);
                lst->entries[i].data=NULL;
                lst->entries[i].size=0;;
            }
        }
        tf_free( lst->entries);
        lst->entries=NULL;
        lst->length=0;
    }
    tf_free(lst);
    LEAVE_FUNC;
}

/**
 *  Set the specific entry to specific value
 *  CAUTION: data will be overwriten
 *  parameters:
 *      lst=> list to be set
 *      idx=> index of entry to be writen
 *      data=> source data to be copied
 *      sz=> length of source data, in byte;
 *  return: 0=>OK, !0=>Error
 */
int set_array_list( array_list* lst, size_t idx, const void* data, size_t sz){
    ENTER_FUNC;
    if( lst==NULL || idx>=lst->length){
        ERR_LOG( "illegal parameters");
        LEAVE_FUNC;
        return -1;
    }
    list_entry *p=NULL;

    if( lst->entries!=NULL){
        lst->entries[idx].size=sz;
        if( lst->entries[idx].data!=NULL){
            lst->entries[idx].data= tf_realloc( lst->entries[idx].data, sz);
        }else{
            lst->entries[idx].data= tf_malloc( sz);
        }
        memcpy( lst->entries[idx].data, data, sz);
    }else{
        ERR_LOG( "NULL ptr error");
    }

    LEAVE_FUNC;
    return -1;
}

/**
 * Get entry at specific index from list
 * parameters: lst=>list
 *  idx=>index
 * return: ptr to the entry, NULL if lst==NULL or idx out of bound
 */
list_entry* get_entry_of_array_list( array_list* lst, size_t idx){
    ENTER_FUNC;
    if( lst==NULL || idx>=lst->length){
        LEAVE_FUNC;
        return NULL;
    }
    LEAVE_FUNC;
    return &(lst->entries[idx]);
}

/**
 * Concact two list, lst1 before lst2
 * A new list will be created
 * parameters: lst1, lst2=> two lists to be concacted;
 * return: 0=>OK, !0=>Error
 */
array_list* concact_array_list( array_list* lst1, array_list* lst2){
    ENTER_FUNC;
    if( lst1==NULL){
        LEAVE_FUNC;
        return copy_array_list( lst2);
    }
    if( lst2==NULL){
        LEAVE_FUNC;
        return copy_array_list( lst1);
    }

    array_list* ret=alloc_array_list( lst1->length + lst2->length);
    if( ret==NULL){
        ERR_LOG( "malloc failed");
        LEAVE_FUNC;
        return NULL;
    }
    size_t base=0;
    int i=0;
    //Copy list 1
    for( i=0; i<lst1->length; ++i){
        ret->entries[i].data= tf_malloc( lst1->entries[i].size);
        ret->entries[i].size= lst1->entries[i].size;
        memcpy( ret->entries[i].data, lst1->entries[i].data, lst1->entries[i].size);
    }
    //Copy list 2
    base=lst1->length;
    for( i=0; i<lst2->length;++i){
        ret->entries[base+i].data= tf_malloc( lst2->entries[i].size);
        ret->entries[base+i].size= lst2->entries[i].size;
        memcpy( ret->entries[base+i].data, lst2->entries[i].data, lst2->entries[i].size);
    }

    LEAVE_FUNC;
    return ret;
}

/**
 * Duplicate a array list into a new copy
 * parameters: lst=> list to be copied;
 * return: ptr to the new copy or NULL is error;
 */
array_list* copy_array_list( array_list* lst){
    ENTER_FUNC;
    if( lst==NULL){
        LEAVE_FUNC;
        return NULL;
    }

    array_list* ret=alloc_array_list( lst->length);
    int i=0;
    for( i=0; i< ret->length; ++i){
        if( lst->entries[i].size>0){
            ret->entries[i].data= tf_malloc( lst->entries[i].size);
            memcpy( ret->entries[i].data, lst->entries[i].data, lst->entries[i].size);
        }else{
            ret->entries[i].data= NULL;
        }
        ret->entries[i].size= lst->entries[i].size;
    }

    LEAVE_FUNC;
    return ret;
}

/**
 * Get the content of specific array list from certain index;
 * parameters: lst=> list to get, index=>index of the node;
 * return: pointer to the content, NULL if erro or empty content;
 */
void* get_from_array_list( array_list* lst, size_t index){
    ENTER_FUNC;
    list_entry* entry=get_entry_of_array_list( lst, index);
    if( entry!=NULL){
        LEAVE_FUNC;
        return (entry->data);
    }
    LEAVE_FUNC;
    return NULL;
}

/**
 * Append one data item to the end of the list;
 * parameters: 
 * return: 0=>OK, !0=>NULL
 */
int push_back_array_list( array_list* lst, const void* data, size_t sz){
    ENTER_FUNC;
    if( lst==NULL || data==NULL || sz==0){
        LEAVE_FUNC;
        return -1;
    }

    if( lst->entries!=NULL){
        list_entry* new_entries=NULL;
        size_t old_entries_size = ( lst->length) * sizeof( list_entry);
        size_t new_entries_size = ( lst->length + 1) * sizeof( list_entry);
        new_entries = ( list_entry*) tf_malloc( new_entries_size );
        if( new_entries!=NULL){
            memcpy( new_entries, lst->entries, old_entries_size);
            free(lst->entries);
            lst->entries=new_entries;
            lst->length += 1;
        }else{
            LEAVE_FUNC;
            return -2;
        }
    }else{
        lst->entries = ( list_entry*) tf_malloc( sizeof( list_entry)); 
        if( lst->entries==NULL){
            LEAVE_FUNC;
            return -3;
        }
        lst->length = 1;
    }

    size_t idx_new = lst->length-1;
    lst->entries[idx_new].data = tf_malloc( sz);
    if( lst->entries[ idx_new].data!=NULL){
        lst->entries[ idx_new].size = sz;
        memcpy( lst->entries[ idx_new].data, data, sz);
    }else{
        LEAVE_FUNC;
        return -3;
    }

    LEAVE_FUNC;
    return 0;
}

/**
 * push one data item to the head of the list;
 * parameters:
 * return: 0=>OK, !0=>Error;
 */
int push_head_array_list( array_list* lst, const void *data, size_t sz){
    ENTER_FUNC;
    if( lst==NULL || data==NULL || sz==0){
        LEAVE_FUNC;
        return -1;
    }

    if( lst->entries!=NULL){
        list_entry* new_entries=NULL;
        size_t old_entries_size = ( lst->length) * sizeof( list_entry);
        size_t new_entries_size = ( lst->length + 1) * sizeof( list_entry);
        new_entries = ( list_entry*) tf_malloc( new_entries_size );
        memcpy( new_entries+1, lst->entries, old_entries_size);
        free(lst->entries);
        lst->entries=new_entries;
        lst->length += 1;
    }else{
        lst->entries = ( list_entry*) tf_malloc( sizeof( list_entry)); 
        lst->length = 1;
    }

    lst->entries[ 0 ].data = tf_malloc( sz);
    if( lst->entries[ 0 ].data!=NULL){
        memcpy( lst->entries[ 0 ].data, data, sz);
    }

    LEAVE_FUNC;
    return 0;
}

/**
 * To process each element of a array_list;
 * parameters: lst=> list to be traversed
 *      func=> call back to be called for each element
 *          If the transform process is fine, 0 will be returned,
 *          else !0 and traversal will be terminated.
 * return: 0=>OK, !0=>Error;
 */
int foreach_array_list( array_list* lst, array_list_foreach_callback func){
    ENTER_FUNC;
    if( lst ==NULL || func==NULL){
        LEAVE_FUNC;
        return -1;
    }

    int i=0;
    int ret=0;
    for( i=0; i<lst->length; ++i){
        if( 0!=(ret=func( lst->entries[i].data))){
            LEAVE_FUNC;
            return ret;
        }
    }
    LEAVE_FUNC;
    return ret;
}

#if defined( __cplusplus)
}   //extern "C"{
#endif

